using System;
using System.Collections.Generic;
using System.Text;
using Algorithms;
using System.Diagnostics;
using System.Drawing;

namespace PetriDiagram.Algorithms
    {
    public class VertexView
        {
        private int _width;

        public int Width
            {
            get
                {
                return _width;
                }
            set
                {
                _width = value;
                }
            }
        private double _pos_x;

        public double Pos_x
            {
            get
                {
                return _pos_x;
                }
            set
                {
                _pos_x = value;
                }
            }
        private double _pos_y;

        public double Pos_y
            {
            get
                {
                return _pos_y;
                }
            set
                {
                _pos_y = value;
                }
            }
        private List<VertexView> _children;

        internal List<VertexView> Children
            {
            get
                {
                return _children;
                }
            set
                {
                _children = value;
                }
            }
        private Vertex _referencedVertex;

        public Vertex ReferencedVertex
            {
            get
                {
                return _referencedVertex;
                }
            set
                {
                _referencedVertex = value;
                }
            }
        }

    public class CoveringTreeView
        {
        private Vertex _treeModel;
        private VertexView _treeView;

        public CoveringTreeView( Vertex root )
            {
            _treeModel = root;
            _treeView = CreateTreeView(_treeModel);
            CalculatePositions( _treeView );
            }

        private VertexView CreateTreeView( Vertex referencedVertex )
            {
            VertexView result = new VertexView();
            result.ReferencedVertex = referencedVertex;
            result.Children = new List<VertexView>();
            foreach( Vertex v in referencedVertex.Children )
                {
                result.Children.Add(CreateTreeView( v ));
                }
            
            result.Width = 0;

            if( result.Children.Count == 0 )
                result.Width = 1;
            else
                {
                foreach( VertexView vv in result.Children )
                    result.Width += vv.Width;
                }
            
            return result;
            }

        private void CalculatePositions( VertexView tree )
            {
            CalculatePositions( tree, 0, 0 );
            }

        private void CalculatePositions( VertexView node, int level, int offsetLeft )
            {
            node.Pos_x = offsetLeft + ( double )node.Width / 2;
            node.Pos_y = level;
            int newOffset = offsetLeft;
            foreach( VertexView vv in node.Children )
                {
                CalculatePositions( vv, level + 1, newOffset );
                newOffset += vv.Width;
                }
            }

        public int GetWidth()
            {
            return _treeView.Width;
            }

        public int GetHeight()
            {
            return GetHeight( _treeView );
            }

        private int GetHeight( VertexView node )
            {
            int result = 0;
            foreach( VertexView vv in node.Children )
                {
                int current = GetHeight( vv );
                if( current > result )
                    result = current;
                }
            return result + 1;
            }

        public VertexView GetRoot()
            {
            return _treeView;
            }

        public void Print( Graphics g )
            {
            Print( g, _treeView );
            }

        private void Print( Graphics g, VertexView node )
            {
            if( node.Width > CoveringTreeUtils.TreeWidthUpperLimit )
                throw new Exception( "The tree is too complex!" );
            int posx = ( int )( node.Pos_x * CoveringTreeUtils.UnitWidth );
            int posy = ( int )( node.Pos_y * CoveringTreeUtils.UnitHeight );

            foreach( VertexView vv in node.Children )
                {
                g.DrawLine( CoveringTreeUtils.Lines, posx + ( CoveringTreeUtils.CellWidth / 2 ), posy + CoveringTreeUtils.CellHeight,
                    ( int )( vv.Pos_x * CoveringTreeUtils.UnitWidth + ( CoveringTreeUtils.CellWidth / 2 ) ),
                    ( int )( vv.Pos_y * CoveringTreeUtils.UnitHeight ) );

                int numTran = vv.ReferencedVertex.ComingTransition;
                int x = ( int )( posx + ( CoveringTreeUtils.CellWidth / 2 ) - ( posx - vv.Pos_x * CoveringTreeUtils.UnitWidth ) / 2 );
                int y = ( int )( posy + CoveringTreeUtils.CellHeight + ( ( CoveringTreeUtils.UnitHeight - CoveringTreeUtils.CellHeight ) / 2 ) );
                x -= CoveringTreeUtils.TranCircleWidth / 2;
                y -= CoveringTreeUtils.TranCircleHeight / 2;

                g.FillEllipse( CoveringTreeUtils.FillBrush, x, y, CoveringTreeUtils.TranCircleWidth,
                    CoveringTreeUtils.TranCircleHeight );
                g.DrawEllipse( CoveringTreeUtils.Lines, x, y, CoveringTreeUtils.TranCircleWidth, 
                    CoveringTreeUtils.TranCircleHeight );
                g.DrawString( "T" + numTran, CoveringTreeUtils.StdFont, 
                    CoveringTreeUtils.StdBrush, x + 3, y + 1 );

                Print( g, vv );
                }

            RectangleF rect = new RectangleF( posx, posy, CoveringTreeUtils.CellWidth, CoveringTreeUtils.CellHeight );
            g.FillRectangle( CoveringTreeUtils.FillBrush, rect );
            g.DrawRectangle( CoveringTreeUtils.Lines, Rectangle.Round( rect ) );
            g.DrawString( node.ReferencedVertex.Marking.ToString(), CoveringTreeUtils.StdFont,
                CoveringTreeUtils.StdBrush, rect, CoveringTreeUtils.Format );
            }
        }
    }
